<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<!-- 
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License. 
-->
<html>
<head>
    <link type="text/css" rel="stylesheet" href="https://struts.apache.org/css/default.css">
    <style type="text/css">
        .dp-highlighter {
            width:95% !important;
        }
    </style>
    <style type="text/css">
        .footer {
            background-image:      url('https://cwiki.apache.org/confluence/images/border/border_bottom.gif');
            background-repeat:     repeat-x;
            background-position:   left top;
            padding-top:           4px;
            color:                 #666;
        }
    </style>
    <link href='https://struts.apache.org/highlighter/style/shCoreStruts.css' rel='stylesheet' type='text/css' />
    <link href='https://struts.apache.org/highlighter/style/shThemeStruts.css' rel='stylesheet' type='text/css' />
    <script src='https://struts.apache.org/highlighter/js/shCore.js' type='text/javascript'></script>
    <script src='https://struts.apache.org/highlighter/js/shBrushPlain.js' type='text/javascript'></script>
    <script src='https://struts.apache.org/highlighter/js/shBrushXml.js' type='text/javascript'></script>
    <script src='https://struts.apache.org/highlighter/js/shBrushJava.js' type='text/javascript'></script>
    <script src='https://struts.apache.org/highlighter/js/shBrushJScript.js' type='text/javascript'></script>
    <script src='https://struts.apache.org/highlighter/js/shBrushGroovy.js' type='text/javascript'></script>
    <script src='https://struts.apache.org/highlighter/js/shBrushBash.js' type='text/javascript'></script>
    <script type="text/javascript">
        SyntaxHighlighter.defaults['toolbar'] = false;
        SyntaxHighlighter.all();
    </script>
    <script type="text/javascript" language="javascript">
        var hide = null;
        var show = null;
        var children = null;

        function init() {
            /* Search form initialization */
            var form = document.forms['search'];
            if (form != null) {
                form.elements['domains'].value = location.hostname;
                form.elements['sitesearch'].value = location.hostname;
            }

            /* Children initialization */
            hide = document.getElementById('hide');
            show = document.getElementById('show');
            children = document.all != null ?
                    document.all['children'] :
                    document.getElementById('children');
            if (children != null) {
                children.style.display = 'none';
                show.style.display = 'inline';
                hide.style.display = 'none';
            }
        }

        function showChildren() {
            children.style.display = 'block';
            show.style.display = 'none';
            hide.style.display = 'inline';
        }

        function hideChildren() {
            children.style.display = 'none';
            show.style.display = 'inline';
            hide.style.display = 'none';
        }
    </script>
    <title>Using Struts and XWork with JSP 2.0 and JSTL 1.1</title>
</head>
<body onload="init()">
<table border="0" cellpadding="2" cellspacing="0" width="100%">
    <tr class="topBar">
        <td align="left" valign="middle" class="topBarDiv" align="left" nowrap>
            &nbsp;<a href="home.html">Home</a>&nbsp;&gt;&nbsp;<a href="faqs.html">FAQs</a>&nbsp;&gt;&nbsp;<a href="cookbook.html">Cookbook</a>&nbsp;&gt;&nbsp;<a href="using-struts-and-xwork-with-jsp-20-and-jstl-11.html">Using Struts and XWork with JSP 2.0 and JSTL 1.1</a>
        </td>
        <td align="right" valign="middle" nowrap>
            <form name="search" action="https://www.google.com/search" method="get">
                <input type="hidden" name="ie" value="UTF-8" />
                <input type="hidden" name="oe" value="UTF-8" />
                <input type="hidden" name="domains" value="" />
                <input type="hidden" name="sitesearch" value="" />
                <input type="text" name="q" maxlength="255" value="" />
                <input type="submit" name="btnG" value="Google Search" />
            </form>
        </td>
    </tr>
</table>

<div id="PageContent">
    <div class="pageheader" style="padding: 6px 0px 0px 0px;">
        <!-- We'll enable this once we figure out how to access (and save) the logo resource -->
        <!--img src="/wiki/images/confluence_logo.gif" style="float: left; margin: 4px 4px 4px 10px;" border="0"-->
        <div style="margin: 0px 10px 0px 10px" class="smalltext">Apache Struts 2 Documentation</div>
        <div style="margin: 0px 10px 8px 10px"  class="pagetitle">Using Struts and XWork with JSP 2.0 and JSTL 1.1</div>

        <div class="greynavbar" align="right" style="padding: 2px 10px; margin: 0px;">
            <a href="https://cwiki.apache.org/confluence/pages/editpage.action?pageId=14253">
                <img src="https://cwiki.apache.org/confluence/images/icons/notep_16.gif"
                     height="16" width="16" border="0" align="absmiddle" title="Edit Page"></a>
            <a href="https://cwiki.apache.org/confluence/pages/editpage.action?pageId=14253">Edit Page</a>
            &nbsp;
            <a href="https://cwiki.apache.org/confluence/pages/listpages.action?key=WW">
                <img src="https://cwiki.apache.org/confluence/images/icons/browse_space.gif"
                     height="16" width="16" border="0" align="absmiddle" title="Browse Space"></a>
            <a href="https://cwiki.apache.org/confluence/pages/listpages.action?key=WW">Browse Space</a>
            &nbsp;
            <a href="https://cwiki.apache.org/confluence/pages/createpage.action?spaceKey=WW&fromPageId=14253">
                <img src="https://cwiki.apache.org/confluence/images/icons/add_page_16.gif"
                     height="16" width="16" border="0" align="absmiddle" title="Add Page"></a>
            <a href="https://cwiki.apache.org/confluence/pages/createpage.action?spaceKey=WW&fromPageId=14253">Add Page</a>
            &nbsp;
            <a href="https://cwiki.apache.org/confluence/pages/createblogpost.action?spaceKey=WW&fromPageId=14253">
                <img src="https://cwiki.apache.org/confluence/images/icons/add_blogentry_16.gif"
                     height="16" width="16" border="0" align="absmiddle" title="Add News"></a>
            <a href="https://cwiki.apache.org/confluence/pages/createblogpost.action?spaceKey=WW&fromPageId=14253">Add News</a>
        </div>
    </div>

    <div class="pagecontent">
        <div class="wiki-content">
            <div id="ConfluenceContent">

<p>S2/X1 and its taglib is oriented towards OGNL, which is using a value stack <br clear="none">
for all action properties. These values are not direct available for the<br clear="none">
expression language of JSP2/JSTL1.1. </p>

<p>However, it's easy to populate the request<br clear="none">
attribute set, with all gettable properties of an action object. You need to provide<br clear="none">
an interceptor that does the job, by register a PreResultListener which is <br clear="none">
invoked after the return of Action.execute() but before the rendering of the result .</p>

<p>The interceptor below is using Jakarta BeanUtils. It first extracts all getters<br clear="none">
of the current action, invokes them one at the time and stores the values into a map.<br clear="none">
Then it iterates over the map and populates the request attribute set.<br clear="none">
<em>The double iteration is not needed, it's just there for clarity.</em></p>
<h2 id="UsingStrutsandXWorkwithJSP2.0andJSTL1.1-classActionPropertyExportInterceptor">class ActionPropertyExportInterceptor</h2>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">
package com.whatever.interceptors;

import org,apache.struts2.StrutsStatics;
import com.opensymphony.xwork.Action;
import com.opensymphony.xwork.ActionInvocation;
import com.opensymphony.xwork.interceptor.AroundInterceptor;
import com.opensymphony.xwork.interceptor.PreResultListener;
import org.apache.commons.beanutils.PropertyUtils;
import javax.servlet.http.HttpServletRequest;
import java.beans.PropertyDescriptor;
import java.util.*;

/**
 * Populates HTTP Request Attributes with all gettable properties of the current action.
 */
public class ActionPropertyExportInterceptor extends AroundInterceptor {
    protected void before(ActionInvocation invocation) throws Exception {
        invocation.addPreResultListener( new PropertyExporter() );
    }
    protected void after(ActionInvocation dispatcher, String result) throws Exception { }

    public static class PropertyExporter implements PreResultListener {
        private static final List   ignore = Arrays.asList(new String[] {"class", "texts"}); //skip getClass,...

        //Invoked after Action.execute() but before Result
        //Calls all getters of the action and insert the values into the request
        public void beforeResult(ActionInvocation invocation, String resultCode) {
            Map                 props   = extractGetterPropertyValues( invocation.getAction() );
            HttpServletRequest  request = getRequest(invocation);
            for (Iterator it = props.entrySet().iterator(); it.hasNext();) {
                Map.Entry   e = (Map.Entry) it.next();
                request.setAttribute((String) e.getKey(), e.getValue());
            }
        }

        public Map extractGetterPropertyValues(Object bean) {
            PropertyDescriptor[]  descr = PropertyUtils.getPropertyDescriptors(bean);
            Map                   props = new HashMap();
            for (int i = 0; i &lt; descr.length; i++) {
                PropertyDescriptor d = descr[i];
                if (d.getReadMethod() == null) continue;
                if (ignore.contains(d.getName())) continue;

                try {
                    props.put(d.getName(), PropertyUtils.getProperty(bean, d.getName()));
                } catch (Exception e) { }
            }
            return props;
        }

        public HttpServletRequest getRequest(ActionInvocation invocation) {
            return (HttpServletRequest) invocation.getInvocationContext().get(WebWorkStatics.HTTP_REQUEST);
        }
    }
}
</pre>
</div></div>

<p>Don't forget to <em>declare</em> the interceptor in your struts.xml file and <em>insert</em> it <br clear="none">
into your interceptor stack.</p>
<h2 id="UsingStrutsandXWorkwithJSP2.0andJSTL1.1-struts.xmlsnippet">struts.xml snippet</h2>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">
&lt;interceptor name="export" class="com.whatever.interceptors.ActionPropertyExportInterceptor" /&gt;
. . .
&lt;interceptor-stack name="standard-interceptors"&gt;
    &lt;interceptor-ref name="timer" /&gt;
    &lt;interceptor-ref name="logger" /&gt;
    &lt;interceptor-ref name="params" /&gt;
*    &lt;interceptor-ref name="export"/&gt;*
    &lt;interceptor-ref name="validateParams"/&gt;
    &lt;interceptor-ref name="awarePlugger" /&gt;
&lt;/interceptor-stack&gt;
</pre>
</div></div>

<p>Your action need to provide getters for all properties that should be exported into the <br clear="none">
request attribute set.</p>
<h2 id="UsingStrutsandXWorkwithJSP2.0andJSTL1.1-classViewUser">class ViewUser</h2>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">
public class ViewUser extends ActionSupport {
    private int                         id;
    private User                        user;

    public String execute() throws Exception {
        user = findUser( getId() );
        return Action.SUCCESS;
    }

    public  int   getId()          {return id;}
    public  void  setId(int id)    {this.id = id;}
*    public  User  getUser()        {return user;}*

    private User  findUser(int id) {...}
}
</pre>
</div></div>

<p>The User class might look like this</p>
<h2 id="UsingStrutsandXWorkwithJSP2.0andJSTL1.1-classUser">class User</h2>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">
import java.util.Date;
public class User {
    private int     id;
    private String  firstName, lastName, email;
    private String  street, zip, city;
    private Date    date;
    
    public String  getFirstName() {return firstName;}
    //..._getters and setters_...
}
</pre>
</div></div>

<p>Finally, using the samples above you can write your JSP2 page like this.</p>
<h2 id="UsingStrutsandXWorkwithJSP2.0andJSTL1.1-ViewUser.jsp">ViewUser.jsp</h2>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">
&lt;%@ taglib prefix="c"   uri="http://java.sun.com/jsp/jstl/core" %&gt;
&lt;%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %&gt;
&lt;%@ taglib prefix="fn"  uri="http://java.sun.com/jsp/jstl/functions" %&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Info about ${user.firstName}&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Info about ${user.firstName} ${user.lastName} [OS:ID=${user.id}]&lt;/h1&gt;
    &lt;table border="1" cellspacing="0" cellpadding="2" width="90%" &gt;
    &lt;tr&gt;
        &lt;th&gt;Name&lt;/th&gt; &lt;td&gt;${user.firstName} ${user.lastName}&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;th&gt;Created&lt;/th&gt; &lt;td&gt;&lt;fmt:formatDate value="${user.date}" pattern="yyyy-MM-dd HH:mm"/&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;th&gt;Email&lt;/th&gt; &lt;td&gt;${user.email}&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;th&gt;Address&lt;/th&gt; &lt;td&gt;${user.street} ${user.zip} ${fn:toUpperCase(user.city)}&lt;/td&gt;
    &lt;/tr&gt;
    &lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
</div></div>

<h2 id="UsingStrutsandXWorkwithJSP2.0andJSTL1.1-DisplayingvalidationerrorswithJSTL">Displaying validation errors with JSTL</h2>

<div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">
&lt;c:if test="${!empty fieldErrors || !empty actionErrors}"&gt;
  &lt;div class="red"&gt;
    &lt;ul&gt;
      &lt;c:forEach items="${fieldErrors}" var="fieldError"&gt;
        &lt;c:forEach items="${fieldError.value}" var="error"&gt;
          &lt;li&gt;${error}&lt;/li&gt;
        &lt;/c:forEach&gt;
      &lt;/c:forEach&gt;
      &lt;c:forEach items="${actionErrors}" var="actionError"&gt;
        &lt;li&gt;${actionError}&lt;/li&gt;
      &lt;/c:forEach&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;/c:if&gt;
</pre>
</div></div></div>
        </div>

        
    </div>
</div>
<div class="footer">
    Generated by CXF SiteExporter
</div>
</body>
</html>
